package org.argus.common.web.core.data;

import cn.hutool.core.collection.CollectionUtil;
import lombok.extern.slf4j.Slf4j;
import org.argus.common.core.annotation.ValueField;
import org.argus.common.core.utils.ReflectUtils;
import org.argus.common.redis.service.RedisService;
import org.argus.common.web.core.data.api.DataValueCacheApi;
import org.argus.common.web.core.data.model.BuildCacheReq;
import org.argus.common.web.core.data.model.BuildCacheResp;
import org.argus.common.web.core.data.model.CacheFieldReq;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.util.*;

/**
 * 自动赋值字段 值分配器
 * 
 * @Author 刘文/Cliff Liu
 * @Date 2024/7/27
 */
@Component
@Slf4j
public class ValueFieldsAssigner {

    @Autowired
    private RedisService redisService;

    @Autowired
    private DataValueCacheApi dataValueCacheApi;

    /**
     * 给目标对象用{@link ValueField}注解的字段赋值
     * 
     * @param targets
     */
    public void assignValue(Object ... targets) {
        if (targets == null || targets.length == 0) {
            return;
        }
        assignValue(Arrays.asList(targets));
    }

    /**
     * 给目标对象列表用{@link ValueField}注解的字段赋值
     * 
     * @param targetList
     */
    public void assignValue(List<?> targetList) {
        if (CollectionUtil.isEmpty(targetList)) {
            return;
        }
        Class<?> clazz = targetList.get(0).getClass();
        Map<Field, ToBeAssignedFieldValues> needRequestValueFields = new HashMap<>();
        for (Field field : ReflectUtils.getAllFields(clazz)) {
            ValueField annotation = field.getDeclaredAnnotation(ValueField.class);
            if (annotation == null) {
                continue;
            }
            for (Object target : targetList) {
                String keyValue = DataValueCacheUtils.getKeyValue(target, annotation);
                // key 值为空， 跳过给注解字段赋值
                if (keyValue == null) {
                    continue;
                }
                String cacheKey = DataValueCacheUtils.buildDataCacheKey(annotation.sourceTable(),
                    annotation.keyColumn(), keyValue, annotation.valueColumn());
                // 缓存命中
                if (redisService.hasKey(cacheKey)) {
                    String value = redisService.getCacheObject(cacheKey);
                    field.setAccessible(true);
                    try {
                        field.set(target, value);
                    } catch (IllegalAccessException e) {
                        log.error("Assign value failed.", e);
                    }
                }
                // 未命中，向data服务下发请求
                else {
                    needRequestValueFields.computeIfAbsent(field, k -> {
                        ToBeAssignedFieldValues toBeAssignedFieldValues = new ToBeAssignedFieldValues();
                        toBeAssignedFieldValues.setField(field);
                        toBeAssignedFieldValues.setAnnotation(annotation);
                        return toBeAssignedFieldValues;
                    });
                    ToBeAssignedFieldValues toBeAssignedFieldValues = needRequestValueFields.get(field);
                    toBeAssignedFieldValues.getKeyTargetMap().computeIfAbsent(cacheKey, k -> new ArrayList<>());
                    toBeAssignedFieldValues.getKeyTargetMap().get(cacheKey).add(target);
                    toBeAssignedFieldValues.getKeys().add(keyValue);
                }
            }
        }

        requestAndAssignValue(needRequestValueFields);
    }

    private void requestAndAssignValue(Map<Field, ToBeAssignedFieldValues> needRequestValueFields) {
        if (CollectionUtil.isEmpty(needRequestValueFields)) {
            log.debug("All fields are hit.");
            return;
        }
        BuildCacheReq buildCacheReq = new BuildCacheReq();
        List<CacheFieldReq> cacheFieldReqs = new ArrayList<>(needRequestValueFields.size());
        for (Map.Entry<Field, ToBeAssignedFieldValues> entry : needRequestValueFields.entrySet()) {
            ToBeAssignedFieldValues toBeAssignedFieldValues = entry.getValue();
            ValueField annotation = toBeAssignedFieldValues.getAnnotation();
            CacheFieldReq cacheFieldReq = new CacheFieldReq();
            cacheFieldReq.setSourceTable(annotation.sourceTable());
            cacheFieldReq.setKeyColumn(annotation.keyColumn());
            cacheFieldReq.setValueColumn(annotation.valueColumn());
            cacheFieldReq.setKeys(toBeAssignedFieldValues.getKeys());
            cacheFieldReqs.add(cacheFieldReq);
        }
        buildCacheReq.setCacheFieldReqs(cacheFieldReqs);
        BuildCacheResp buildCacheResp = dataValueCacheApi.buildCache(buildCacheReq);
        if (CollectionUtil.isEmpty(buildCacheResp.getKeyValueMap())) {
            log.warn("Requested {} fields value but returned none.", buildCacheReq.getCacheFieldReqs().size());
            return;
        }

        // 将从数据服务获取到的值赋值给目标对象
        needRequestValueFields.forEach((field, toBeAssignedFieldValues) -> {
            toBeAssignedFieldValues.getKeyTargetMap().forEach((cacheKey, targets) -> {
                String value = buildCacheResp.getKeyValueMap().get(cacheKey);
                if (value == null) {
                    return;
                }
                field.setAccessible(true);
                try {
                    for (Object target : targets) {
                        field.set(target, value);
                    }
                } catch (IllegalAccessException e) {
                    log.error("Assign value failed.", e);
                }
            });
        });
    }

}
